package whut.problem;

/* author: gandhi - gandhi.mtm [at] gmail [dot] com - Depok, Indonesia */

// this is the problem to be solved
// to find an x and a y that minimize the function below:
// f(x, y) = (2.8125 - x + x * y^4)^2 + (2.25 - x + x * y^2)^2 + (1.5 - x + x*y)^2
// where 1 <= x <= 4, and -1 <= y <= 1

// you can modify the function depends on your needs
// if your problem space is greater than 2-dimensional space
// you need to introduce a new variable (other than x and y)


import whut.commons.IProblemSet;
import whut.commons.Location;

public class ProblemSet implements IProblemSet {
	public final double LOC_LOW = -1;
	public final double LOC_HIGH = 1;
	public final double VEL_LOW = -1;
	public final double VEL_HIGH = 1;
	public final int PROBLEM_DIMENSION = 2;

	public static final double ERR_TOLERANCE = 1E-20; // the smaller the tolerance, the more accurate the result,
	                                                  // but the number of iteration is increased

	public void setSelected(int selected) {
		this.selected = selected;
	}

	private int selected = 0;

	@Override
	public double getLOC_LOW() {
		return LOC_LOW;
	}

	@Override
	public double getLOC_HIGH() {
		return LOC_HIGH;
	}

	@Override
	public double getVEL_LOW() {
		return VEL_LOW;
	}

	@Override
	public double getVEL_HIGH() {
		return VEL_HIGH;
	}

	@Override
	public double getERR_TOLERANCE() {
		return ERR_TOLERANCE;
	}

	@Override
	public int getPROBLEM_DIMENSION() {
		return PROBLEM_DIMENSION;
	}

	@Override
	public int getLOC_DIMENSION() {
		return PROBLEM_DIMENSION;
	}

	@Override
	public double evaluate(Location location) {
		double result = 0;

		switch (selected){
			case 0:
				double x = location.getLoc()[0]; // the "x" part of the location
				double y = location.getLoc()[1]; // the "y" part of the location
				result = Math.pow(2.8125 - x + x * Math.pow(y, 4), 2) +
						Math.pow(2.25 - x + x * Math.pow(y, 2), 2) +
						Math.pow(1.5 - x + x * y, 2);
				break;
			case 1://sphere函数    简单单峰函数
				for (int i = 0; i < PROBLEM_DIMENSION; i++) {
					result = result + location.getLoc()[i] * location.getLoc()[i];
				}
				break;
			case 2://Rosenbrock函数  复杂单峰函数
				for (int i = 0; i < PROBLEM_DIMENSION - 1; i++) {
					result = result + 100 * (location.getLoc()[i + 1] - location.getLoc()[i] * location.getLoc()[i])
							* (location.getLoc()[i + 1] - location.getLoc()[i] * location.getLoc()[i]) +
							(location.getLoc()[i] - 1) * (location.getLoc()[i] - 1);
				}
				break;
			case 3://Rastrigin函数    复杂多峰函数
				for (int i = 0; i < PROBLEM_DIMENSION; i++) {
					result = result + location.getLoc()[i] * location.getLoc()[i] -
							10 * Math.cos(2 * Math.PI * location.getLoc()[i]) + 10;
				}
				break;
			case 4://Griewank函数   简单多峰函数
				double result1 = 0, result2 = 1;
				for (int i = 0; i < PROBLEM_DIMENSION; i++) {
					result1 = result1 + location.getLoc()[i] * location.getLoc()[i];
				}
				result1 = result1 / 4000;
				for (int k = 0; k < PROBLEM_DIMENSION; k++) {
					result2 = result2 * Math.cos(location.getLoc()[k] / (Math.sqrt(k + 1)));
				}
				result = result1 - result2 + 1;
				break;
			case 5:

				break;
			case 6:

				break;
		}

		location.setExtended(new double[]{result});
		
		return result;
	}
}
